home *** CD-ROM | disk | FTP | other *** search
/ Chip: 2005 Utilities / CHIP Utilities 2005 / CHIP Utilities 2005.iso / dosapps / freedos / nls / fixstrs.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-04-11  |  22.5 KB  |  871 lines

  1. /*
  2. This program will read in the language file and create three files:
  3.  
  4. strings.dat - contains all the strings. will be appended on the end
  5.               of the exe file.
  6.  
  7. strings.h - include file that contains all the defines.
  8.  
  9. strings.log - contains all warnings of the local LNG file, the
  10.               which resources (strings) are missing from it and
  11.               which ones are not specified in the DEFAULT.LNG, thus,
  12.               are most likely unknown to FreeCOM
  13.               This file is present only, if there are such warnings,
  14.               but no message is printed onto screen!
  15.  
  16. Then, if the "/lib" option is present, it creates the
  17. STRINGS library source files into the subdirectory STRINGS.
  18.  
  19. There are two input files:
  20. DEFAULT.LNG and the language file passed as argument to FIXSTRS.
  21. DEFAULT.LNG has two meanings making it a fundamental file, which
  22. ensures the integrity of the multi-language support of FreeCOM:
  23.  
  24. 1) The order of strings noted in DEFAULT.LNG will be kept the same in
  25.     all *.LNG files.
  26. 2) If an individual *.LNG file does not define a certain string, its
  27.     contents is taken from the DEFAULT file.
  28.  
  29. Because each string is assigned a certain number and only this number
  30. is known to FreeCOM internally, especially meaning 1) will ensure that
  31. each STRINGS.DAT assigns the same semantic to those numbers.
  32.  
  33. The STRINGS.DAT file generated in the following steps:
  34. 1) DEFAULT.LNG is read; all strings are copied into memory,
  35.     the string_index_t array is generated with these strings.
  36.     At the end, one could generate STRINGS.DAT with all the default
  37.     strings, which are usually in English.
  38. 2) the specified *.LNG file is read; if a string is defined in this file,
  39.     too, its contents overwrites the default string already present.
  40.     New strings are assigned a new number above the already allocated
  41.     string numbers.
  42. 3) Now all strings are known, there sizes and offsets within the file,
  43.     the STRINGS.DAT and STRINGS.H files are generated.
  44.  
  45. 2000/07/09 ska
  46. chg: to read STRINGS.H to keep the same order of strings
  47. chg: to let STRINGS.TXT read only once (temporary binary file)
  48. chg: The format of STRINGS.DAT has been changed in order to support
  49.     different languages _without_ recompiling.
  50.  
  51. 2000/07/11 ska
  52. chg: To use STRINGS.H to keep up the order becomes problematic, as this
  53.     file is regenerated each time FIXSTRS is run. On failure, this
  54.     file is destroyed. Therefore STRINGS.TXT will be renamed into
  55.     DEFAULT.LNG and is used to a) specify the order and, if missing,
  56.     the default string text.
  57.  
  58. 2001/03/15 ska
  59. add: version number of strings and logfile entries
  60. */
  61.  
  62.  
  63. #include <ctype.h>
  64. #include <dir.h>
  65. #include <stdio.h>
  66. #include <string.h>
  67. #include <stdlib.h>
  68.  
  69. #undef DEBUG
  70. #include "../config.h"
  71. #include "../include/strings.typ"
  72. #include "../include/resource.h"
  73. #include "../include/keys.h"
  74.  
  75. #define logfile "STRINGS.LOG"
  76. #define fDAT "STRINGS.DAT"
  77. #define fTXT "DEFAULT.LNG"
  78. #define fH "STRINGS.H"
  79. #define fEXT ".LNG"
  80. #define fDMAKEFILE "makefile"
  81. #define fTCMAKEFILE "strings.rsp"
  82.  
  83. typedef enum STATE {
  84.      LOOKING_FOR_START
  85.     ,GETTING_PROMPT_LINE_1
  86.     ,GETTING_PROMPT_LINE_2
  87.     ,GETTING_STRING
  88. } read_state;
  89.  
  90. #define MAXSTRINGS       256
  91.  
  92. #define VERSION_MISMATCH 128
  93. #define VALIDATION_MISMATCH 64
  94.  
  95. const char id[]="FreeDOS STRINGS v";
  96.     /* all prompts within *.LNG files start with */
  97. const char promptID[] = "PROMPT_";
  98. #define promptIDlen (sizeof(promptID) - 1)
  99.  
  100. #define STRINGLIB_DIR "STRINGS"
  101. #define stringdir cfile
  102. #define cfilename (&cfile[sizeof(STRINGLIB_DIR)])
  103. #define cfmt "str%04x.c"
  104. #define objfmt "str%04x.obj"
  105. char cfile[] = STRINGLIB_DIR "\0str45678.obj";
  106.  
  107. /*
  108.     Implementation details about to cache the strings within memory:
  109.  
  110.     + Because the strings are currently entitled to fit into 64KB,
  111.         they should fit into memory during runtime of this program.
  112.         Therefore FIXSTRS will be compiled in Compact memory model.
  113.  
  114. */
  115.  
  116. typedef struct {
  117.     char *text;
  118.     int length;
  119. } dynstring;
  120. typedef struct {
  121.     const int keycode;
  122.     const char * const keyname;
  123. } symKey;
  124.  
  125. FILE *log = 0;
  126.  
  127. int in_file = 0;
  128. string_index_t string[MAXSTRINGS];
  129. struct {
  130.     int file;                /* bitfield: #0 -> DEFAULT, #1 -> special LNG file
  131.                                 meaning: present in particular file */
  132.     char *name;                /* name of string */
  133.     char *text;                /* text of this string */
  134.     int version;
  135.     char *vstring;            /* validation string */
  136. } strg[MAXSTRINGS];
  137. string_count_t cnt = 0;        /* current string number */
  138. string_count_t maxCnt = 0;    /* number of strings within array */
  139.  
  140. #if sizeof(char*) != 4
  141. #error "This program must be compiled with far data pointers"
  142. #endif
  143.  
  144. char temp[1024];
  145.  
  146. static const char besFromChar[] =
  147.  "abcdefghijklmnopqrstuvwxyz,.[{}]\\?0";
  148. static const char besToChar[] =
  149.  "\a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z,.[{}]\\?";
  150.  
  151. symKey symkeys[] = {        /* symbolic keynames, uppercased! */
  152.      { KEY_CTL_C,    "BREAK" }        /* Pseudo-^Break */
  153.     ,{ KEY_CTL_C,    "CBREAK" }
  154.     ,{ KEY_NL,    "LF" }
  155.     ,{ KEY_NL,    "NL" }
  156.     ,{ KEY_NL,    "LINEFEED" }
  157.     ,{ KEY_NL,    "NEWLINE" }
  158.     ,{ KEY_CR,    "CR" }
  159.     ,{ KEY_CR,    "ENTER" }
  160.     ,{ KEY_ESC,    "ESC" }
  161.     ,{ KEY_ESC,    "ESCAPE" }
  162.     ,{ ASCIICODE('\f'),    "FF" }
  163.     ,{ ASCIICODE('\f'),    "FORMFEED" }
  164.     ,{ ASCIICODE('\a'),    "ALARM" }
  165.     ,{ ASCIICODE('\a'),    "BELL" }
  166.     ,{ ASCIICODE('\a'),    "BEEP" }
  167.     ,{ KEY_BS,    "BS" }
  168.     ,{ KEY_BS,    "BACKSPACE" }
  169.     ,{ KEY_TAB,    "HT" }
  170.     ,{ KEY_TAB,    "TAB" }
  171.     ,{ ASCIICODE('\v'),    "VT" }
  172.     ,{ ASCIICODE('\0'),    "NUL" }
  173.     ,{ ASCIICODE('\x1'),    "SOH" }
  174.     ,{ ASCIICODE('\x2'),    "STX" }
  175.     ,{ ASCIICODE('\x3'),    "ETX" }
  176.     ,{ ASCIICODE('\x4'),    "EOT" }
  177.     ,{ ASCIICODE('\x5'),    "ENQ" }
  178.     ,{ ASCIICODE('\x6'),    "ACK" }
  179.     ,{ ASCIICODE('\x7'),    "BEL" }
  180.         /* 8 -> BS, 9 -> HT, A -> LF, B ->VT, C -> FF, D -> CR */
  181.     ,{ ASCIICODE('\xe'),    "SO" }
  182.     ,{ ASCIICODE('\xf'),    "SI" }
  183.     ,{ ASCIICODE('\x10'),    "DLE" }
  184.     ,{ ASCIICODE('\x11'),    "DC1" }
  185.     ,{ ASCIICODE('\x12'),    "DC2" }
  186.     ,{ ASCIICODE('\x13'),    "DC3" }
  187.     ,{ ASCIICODE('\x14'),    "DC4" }
  188.     ,{ ASCIICODE('\x15'),    "NAK" }
  189.     ,{ ASCIICODE('\x16'),    "SYN" }
  190.     ,{ ASCIICODE('\x17'),    "ETB" }
  191.     ,{ ASCIICODE('\x18'),    "CAN" }
  192.     ,{ ASCIICODE('\x19'),    "EM" }
  193.     ,{ ASCIICODE('\x1a'),    "SUB" }
  194.         /* 1b -> ESC */
  195.     ,{ ASCIICODE('\x1c'),    "FS" }
  196.     ,{ ASCIICODE('\x1d'),    "GS" }
  197.     ,{ ASCIICODE('\x1e'),    "RS" }
  198.     ,{ ASCIICODE('\x1f'),    "US" }
  199.  
  200.     ,{ KEY_F1, "F1" }
  201.     ,{ KEY_F2, "F2" }
  202.     ,{ KEY_F3, "F3" }
  203.     ,{ KEY_F4, "F4" }
  204.     ,{ KEY_F5, "F5" }
  205.     ,{ KEY_F6, "F6" }
  206.     ,{ KEY_F7, "F7" }
  207.     ,{ KEY_F8, "F8" }
  208.     ,{ KEY_F9, "F9" }
  209.     ,{ KEY_F10, "F10" }
  210.     ,{ KEY_F11, "F11" }
  211.     ,{ KEY_F12, "F12" }
  212.  
  213.     ,{ KEY_LEFT, "LEFT" }
  214.     ,{ KEY_RIGHT, "RIGHT" }
  215.     ,{ KEY_UP, "UP" }
  216.     ,{ KEY_DOWN, "DOWN" }
  217.     ,{ KEY_INS, "INS" }
  218.     ,{ KEY_DEL, "DEL" }
  219.     ,{ KEY_HOME, "HOME" }
  220.     ,{ KEY_END, "END" }
  221.     ,{ KEY_PUP, "PUP" }
  222.     ,{ KEY_PDOWN, "PDOWN" }
  223.  
  224.     ,{ 0, ""}
  225. };
  226.  
  227.  
  228.     /* keep it a single-file project */
  229. #include "../lib/res_w.c"
  230.  
  231. /*
  232.  * Append the passed in string onto strg[cnt].text
  233.  */
  234. #define app(s)    appStr(text, (s))
  235. #define appStr(vs,s) appMem((vs), (s), strlen((s)))
  236. #define appMem(vs,s,l) appMem_(&(vs), (s), (l))
  237. int appMem_(dynstring *vs, char *s, int length)
  238. {
  239.     if((vs->text = realloc(vs->text, 1 + vs->length + length)) != 0) {
  240.         char *p;
  241.  
  242.         p = vs->text + vs->length;
  243.         memcpy(p, s, length);
  244.         p[length] = 0;
  245.         vs->length += length;
  246.         return 1;
  247.     }
  248.  
  249.     fputs("Out of memory\n", stderr);
  250.     return 0;
  251. }
  252.  
  253. unsigned fromxdigit(int ch)
  254. {    if(isdigit(ch))
  255.         return ch - '0';
  256.     return toupper(ch) - 'A';
  257. }
  258.  
  259. #define join(s1,s2)    strcpy(stpcpy(temp, s1), s2);
  260. void pxerror(const char * const msg1, const char * const msg2)
  261. {    join(msg1, msg2);
  262.     perror(temp);
  263. }
  264.  
  265. void dumpCh(FILE * const f, const int ch)
  266. {    static const char from[] = "'\\\n\t\a\b\f";
  267.     static const char to[]   = "'\\ntabf0";
  268.     const char *p;
  269.  
  270.     if((p = strchr(from, ch)) != 0)
  271.         fprintf(f, "'\\%c'", to[(int)(p - from)]);
  272.     else if(isprint(ch))
  273.         fprintf(f, " '%c'", ch);
  274.     else fprintf(f, "0x%02x", ch);
  275. }
  276.  
  277. void dumpString(const int stringId)
  278. {    FILE *f;
  279.     int len, cnt;
  280.     unsigned char *p;
  281.  
  282.     sprintf(cfilename, cfmt, stringId);
  283.     if((f = fopen(cfile, "wt")) == NULL) {
  284.         pxerror("creating ", cfile);
  285.         exit(102);
  286.     }
  287.     fprintf(f, "const char %s[] = {\n\t", strg[stringId].name);
  288.     p = (unsigned char*)strg[stringId].text;
  289.     if(string[stringId].size > 1) {
  290.         for(len = string[stringId].size, cnt = 9; --len;) {
  291.             char ch;
  292.  
  293.             dumpCh(f, ch = *p++);
  294.             putc(',', f);
  295.             putc(' ', f);
  296.             if(--cnt == 0 || ch == '\n') {
  297.                 putc('\n', f);
  298.                 putc('\t', f);
  299.                 cnt = 9;
  300.             }
  301.         } 
  302.     }
  303.     if(string[stringId].size)
  304.         dumpCh(f, *p);
  305.     fputs("\n};\n", f);
  306.     fclose(f);
  307. }
  308.  
  309.  
  310. /* map a backslash sequence */
  311. int mapBSEscape(char ** const s)
  312. {    char *p, *q;
  313.     int ch;
  314.  
  315.     p = *s;
  316.     if((ch = *p++) == 0)    /* Don't advance pointer */
  317.         return 0;
  318.  
  319.     if(ch == 'x') {    /* Hexadecimal */
  320.         if(isxdigit(*p)) {
  321.             ch = fromxdigit(*p++);
  322.             if(isxdigit(*p))
  323.                 ch = (ch << 4) | fromxdigit(*p++);
  324.         } else
  325.             ch = 0;
  326.     } else if((q = strchr(besFromChar, ch)) != 0) {
  327.         ch = besToChar[(unsigned)(q - besFromChar)];
  328.     } /* else  ch remains the character behind the backslash */
  329.  
  330.     *s = p;            /* Advance pointer */
  331.     return ch;
  332. }
  333.  
  334. /* map a symbolic key */
  335. int mapSymKey(char * const p)
  336. {    symKey *q;
  337.  
  338.     strupr(p);        /* Uppercase here to speed up process later */
  339.     q = symkeys;
  340.     do if(strcmp(q->keyname, p) == 0)    /* found */
  341.         break;
  342.     while((q++)->keycode);
  343.  
  344.     return q->keycode;
  345. }
  346.  
  347. int loadFile(const char * const fnam)
  348. {    unsigned long linenr;
  349.     char *p;
  350.     read_state state = LOOKING_FOR_START;
  351.     FILE *fin;
  352.     dynstring text;            /* Current text */
  353.     dynstring vstring;        /* Validation string */
  354.     int version;
  355.  
  356.     text.text = vstring.text = 0;
  357.  
  358. printf("FIXSTRS: loading file %s\n", fnam);
  359.  
  360.     join(fnam, fEXT);
  361.     if((fin = fopen(fnam, "rt")) == NULL
  362.      && (fin = fopen(temp, "rt")) == NULL) {
  363.         pxerror("opening ", fnam);
  364.         return 33;
  365.     }
  366.  
  367.     linenr = 0;
  368.     while (fgets(temp, sizeof(temp), fin)) {
  369.         ++linenr;
  370.         p = strchr(temp, '\0');
  371.         if(p[-1] != '\n') {
  372.             fprintf(stderr, "Line %lu too long\n", linenr);
  373.             return 41;
  374.         }
  375.             /* Cut trailing control characters */
  376.         while (--p >= temp && *p < ' ');
  377.         p[1] = '\0';
  378.  
  379.         switch (state) {
  380.         case LOOKING_FOR_START:
  381.             switch(*temp) {
  382.             case ':': {
  383.                 char *vers;
  384.  
  385.                 if((vers = strchr(temp + 1, '#')) != 0) {
  386.                     *vers = '\0';
  387.                 }
  388.                 /* Locate the string name */
  389.                 for(cnt = 0; cnt < maxCnt; ++cnt)
  390.                     if(strcmp(strg[cnt].name, temp + 1) == 0)
  391.                         goto strnameFound;
  392.                 /* string name was not found --> create a new one */
  393.                 ++maxCnt;
  394.             strnameFound:
  395.                 if(!strg[cnt].name) {
  396.                     if((strg[cnt].name = strdup(temp + 1)) == 0) {
  397.                         fputs("Out of memory\n", stderr);
  398.                         return 80;
  399.                     }
  400.                 }
  401.                 vstring.length = text.length = 0;
  402.                 version = (vers && *++vers)? atoi(vers): 0;
  403.                 if(memcmp(strg[cnt].name, promptID, promptIDlen) == 0)
  404.                     state = GETTING_PROMPT_LINE_1;
  405.                 else
  406.                     state = GETTING_STRING;
  407.             }
  408.             break;
  409.  
  410.             default:
  411.                 while(p >= temp && isspace(*p)) --p;
  412.                 if(p >= temp) {
  413.                     fprintf(stderr, "Syntax error in line #%lu\n", linenr);
  414.                     return 44;
  415.                 }
  416.                 /** fall through **/
  417.             case '\0': case '#':
  418.                 break;
  419.             }
  420.         break;
  421.  
  422.         case GETTING_PROMPT_LINE_1:
  423.             {
  424.                 char *p, *q, len;
  425.                 int ch;
  426.  
  427.                 if((*temp == '.' || *temp == ',') && (temp[1] == '\0')) {
  428.                     fprintf(stderr, "%s: %s: prompt syntax error\n"
  429.                      , fnam, strg[cnt].name);
  430.                     return 41;
  431.                 }
  432.                 q = p = temp;
  433.                 while((ch = *p++) != 0)
  434.                     switch(ch) {
  435.                     case '\\':
  436.                         if(*p && (*q++ = mapBSEscape(&p)) == 0) {
  437.                             fprintf(stderr
  438.                              , "%s: %s: ASCII(0) is no valid key\n"
  439.                              , fnam, strg[cnt].name);
  440.                             return 49;
  441.                         }
  442.                         break;
  443.                     case '{':
  444.                         {    char *h;
  445.                             int thisCh;
  446.  
  447.                             if((p = strchr(h = p, '}')) == 0) {
  448.                                 fprintf(stderr
  449.                                  , "%s: %s: invalid symbolic key\n"
  450.                                  , fnam, strg[cnt].name);
  451.                                 return 46;
  452.                             }
  453.                             *p++ = 0;
  454.                             if((thisCh = mapSymKey(h)) == 0) {
  455.                                 fprintf(stderr
  456.                                  , "%s: %s: unknown symbolic key\n"
  457.                                  , fnam, strg[cnt].name);
  458.                                 return 47;
  459.                             }
  460.                             if(thisCh >= 256) {
  461.                                 fprintf(stderr
  462.                                  , "%s: %s: non-ASCII keys not supported, yet\n"
  463.                                  , fnam, strg[cnt].name);
  464.                                 return 55;
  465.                             }
  466.                             *q++ = thisCh;
  467.                         }
  468.                         break;
  469.                     case '[':
  470.                         fprintf(stderr
  471.                          , "%s: %s: brackets are not supported, yet\n"
  472.                          , fnam, strg[cnt].name);
  473.                         return 48;
  474.  
  475.                     default:
  476.                         *q++ = ch;
  477.                         break;
  478.                     }
  479.                 *q = 0;
  480.                 if(q == temp) {
  481.                     fprintf(stderr
  482.                      , "%s: %s: empty key sequence\n"
  483.                      , fnam, strg[cnt].name);
  484.                      return 52;
  485.                 }
  486.                 if((unsigned)(q - temp) > 255) {
  487.                     fprintf(stderr
  488.                      , "%s: %s: too many keys\n"
  489.                      , fnam, strg[cnt].name);
  490.                      return 55;
  491.                 }
  492.                 len = (char)(q - temp);
  493.                     /* Prompts are PStrings in this form:
  494.                         LKKKKMMMM
  495.                         where number of K's == number of M's == L
  496.                         K -> key (1..255); M -> metakey (range 1..26);
  497.                         0 < L < 256
  498.                     */
  499.                 if(!appMem(text, &len, 1) || !app(temp))
  500.                     return 42;
  501.                 state = GETTING_PROMPT_LINE_2;
  502.             }
  503.             break;
  504.  
  505.  
  506.         case GETTING_PROMPT_LINE_2:
  507.         {
  508.             char *p, *q;
  509.  
  510.             if ((*temp == '.' || *temp == ',') && (temp[1] == '\0')) {
  511.                 fprintf(stderr, "%s: %s: prompt syntax error\n"
  512.                  , fnam, strg[cnt].name);
  513.                 return 43;
  514.             }
  515.  
  516.             p = q = temp;
  517.             while((*q = *p++) != 0)
  518.                 if(*q >= 'a' && *q <= 'z') {
  519.                     *q++ -= 'a' - 1;    /* valid metakey */
  520.                 } else if(!isspace(*q)) {
  521.                     fprintf(stderr, "%s: %s: invalid target metakey\n"
  522.                      , fnam, strg[cnt].name);
  523.                     return 44;
  524.                 }
  525.             if((unsigned)(q - temp) + 1 != text.length) {
  526.                 fprintf(stderr
  527.                  , "%s: %s: number of metakeys does not match input keys\n"
  528.                  , fnam, strg[cnt].name);
  529.                 return 53;
  530.             }
  531.             if(!app(temp))
  532.                 return 54;
  533.             state = GETTING_STRING;
  534.             break;
  535.         }
  536.  
  537.  
  538.         case GETTING_STRING:
  539.             if ((*temp == '.' || *temp == ',') && (temp[1] == '\0')) {
  540.                 if (*temp == ',' &&    text.length
  541.                  && text.text[text.length - 1] == '\n') {
  542.                      /* Cut the text as there is to always be a '\0' at the
  543.                         end of the string */
  544.                      text.text[--text.length] = '\0';
  545.                 }
  546.                 state = LOOKING_FOR_START;
  547.                 /* Apply the cached text */
  548.                 strg[cnt].file |= in_file;
  549.                 if((!strg[cnt].version || strg[cnt].version == version)
  550.                  && (!strg[cnt].vstring        /* no old string */
  551.                   || (vstring.text
  552.                       && strcmp(strg[cnt].vstring, vstring.text) == 0))) {
  553.                     /* OK -> replace it */
  554.                     strg[cnt].version = version;
  555.                     free(strg[cnt].text);
  556.                     strg[cnt].text = text.text;
  557.                     string[cnt].size = text.length + 1;
  558.                     free(strg[cnt].vstring);
  559.                     strg[cnt].vstring = vstring.text;
  560.                     /* Mark where this entry has been found */
  561.                     text.text = vstring.text = 0;
  562.                 } else {
  563.                     if(strg[cnt].version && strg[cnt].version != version)
  564.                         strg[cnt].file |= VERSION_MISMATCH;
  565.                     if(strg[cnt].vstring
  566.                       && (!vstring.text
  567.                        || strcmp(strg[cnt].vstring, vstring.text) != 0))
  568.                         strg[cnt].file |= VALIDATION_MISMATCH;
  569.                     /* Failed -> ignore the read text */
  570.                     free(text.text);
  571.                     free(vstring.text);
  572.                     text.text = vstring.text = 0;
  573.                 }
  574.             } else {
  575.                 char *p, *q, ch;
  576.                 /* Fetch the '%' format sequences */
  577.                 q = temp - 1;
  578.                 while((p = strchr(q + 1, '%')) != 0) {
  579.                     if((q = strpbrk(p, "%diouxXfegEGcsnp")) == 0)
  580.                         q = strchr(p, '\0') - 1;
  581.                     if(!appMem(vstring, p, (unsigned)(q - p) + 2))
  582.                         return 51;
  583.                 }
  584.                 /* Replace backslash escape sequences */
  585.                 p = q = temp;
  586.                 while((ch = *p++) != 0) {
  587.                     if(ch != '\\')
  588.                         *q++ = ch;
  589.                     else if(!*p) goto noAppendNL;
  590.                     else *q++ = mapBSEscape(&p);
  591.                 }
  592.                 *q++ = '\n';
  593. noAppendNL:
  594.                 if(!appMem(text, temp, (unsigned)(q - temp)))
  595.                     return 82;
  596.             }
  597.         break;
  598.         }
  599.     }
  600.     if(ferror(fin)) {
  601.         pxerror("reading ", fnam);
  602.         return 34;
  603.     }
  604.     fclose(fin);
  605.     if(state != LOOKING_FOR_START) {
  606.         fprintf(stderr, "%s: Last string not terminated\n", fnam);
  607.         return 40;
  608.     }
  609.  
  610.     return 0;
  611. }
  612.  
  613.  
  614. int main(int argc, char **argv)
  615. {
  616.     FILE *dat, *inc;
  617.     int rc;
  618.     unsigned long size;
  619.     string_count_t cnt;        /* current string number */
  620.     string_size_t lsize;
  621.     int makeLib = 0;
  622.  
  623.     unlink(logfile);
  624.  
  625.     if(argv[1] && stricmp(argv[1], "/lib") == 0) {
  626.         --argc;
  627.         ++argv;
  628.         makeLib = 1;
  629.     }
  630.  
  631.     if(argc > 2) {
  632.         puts("FIXSTRS - Generate STRINGS.DAT and STRINGS.H for a language\n"
  633.             "Useage: FIXSTRS [/lib] [language]\n"
  634.             "\tIf no language is specified, only the default strings are read.\n"
  635.             "\tThe <language>.LNG file must reside in the current directory.\n"
  636.             "Note: DEFAULT.LNG must be present in the current directory, too.");
  637.         return 127;
  638.     }
  639.  
  640.  
  641.     in_file = 1;
  642.     if((rc = loadFile(fTXT)) != 0)
  643.         return rc;
  644.     in_file = 2;
  645.     if(argc > 1 && (rc = loadFile(argv[1])) != 0)
  646.         return rc;
  647.  
  648. /* Now all the strings are cached into memory */
  649.  
  650.     if(maxCnt < 2) {
  651.         fputs("No string definition found.\n", stderr);
  652.         return 43;
  653.     }
  654.  
  655.     /* Create the LOG file */
  656.     if(argc > 1) {        /* Only if a local LNG file was specified */
  657.         log = NULL;            /* No LOG entry til this time */
  658.         for(cnt = 0; cnt < maxCnt; ++cnt) {
  659.             switch(strg[cnt].file & 3) {
  660.             case 0:        /* Er?? */
  661.                 fputs("Internal error assigned string has no origin?!\n"
  662.                  , stderr);
  663.                 return 99;
  664.             case 1:        /* DEFAULT.LNG only */
  665.                 if(!log && (log = fopen(logfile, "wt")) == NULL) {
  666.                     fprintf(stderr, "Cannot create logfile: '%s'\n"
  667.                      , logfile);
  668.                     goto breakLogFile;
  669.                 }
  670.                 fprintf(log, "%s: Missing from local LNG file\n"
  671.                  , strg[cnt].name);
  672.                 break;
  673.             case 2:        /* local.LNG only */
  674.                 if(!log && (log = fopen(logfile, "wt")) == NULL) {
  675.                     fprintf(stderr, "Cannot create logfile: '%s'\n"
  676.                      , logfile);
  677.                     goto breakLogFile;
  678.                 }
  679.                 fprintf(log, "%s: No such string resource\n"
  680.                  , strg[cnt].name);
  681.                 break;
  682.             case 3:        /* OK */
  683.                 break;
  684.             }
  685.             if(strg[cnt].file & VERSION_MISMATCH) {
  686.                 if(!log && (log = fopen(logfile, "wt")) == NULL) {
  687.                     fprintf(stderr, "Cannot create logfile: '%s'\n"
  688.                      , logfile);
  689.                     goto breakLogFile;
  690.                 }
  691.                 fprintf(log, "%s: Version mismatch\n"
  692.                  , strg[cnt].name);
  693.             }
  694.             if(strg[cnt].file & VALIDATION_MISMATCH) {
  695.                 if(!log && (log = fopen(logfile, "wt")) == NULL) {
  696.                     fprintf(stderr, "Cannot create logfile: '%s'\n"
  697.                      , logfile);
  698.                     goto breakLogFile;
  699.                 }
  700.                 fprintf(log, "%s: Validation mismatch\n"
  701.                  , strg[cnt].name);
  702.             }
  703.         }
  704.  
  705.         if(log)
  706.             fclose(log);
  707.     }
  708. breakLogFile:
  709.  
  710.     /* 1. Adjust the offset and generate the overall size */
  711.     for(size = string[0].size, cnt = 1; cnt < maxCnt; ++cnt) {
  712.         string[cnt].index = string[cnt-1].index + string[cnt-1].size;
  713.         size += string[cnt].size;
  714.     }
  715.  
  716.     if(size >= 0x10000ul - sizeof(string_index_t) * maxCnt) {
  717.         fputs("Overall size of strings exceeds 64KB limit\n", stderr);
  718.         return 44;
  719.     }
  720.  
  721.     /* 2. Open STRINGS.DAT and STRINGS.H and dump control information */
  722.     if ((dat = fopen(fDAT,"wb")) == NULL) {
  723.         perror("creating " fDAT);
  724.         return 36;
  725.     }
  726.     if ((inc = fopen(fH,"wt")) == NULL) {
  727.         perror("creating " fH);
  728.         return 37;
  729.     }
  730.  
  731. puts("FIXSTRS: building STRINGS resource");
  732.  
  733.     fputs("/*\n"
  734.         " * This file was automatically generated by FIXSTRS.\n"
  735.         " * Any modifications will be lost next time this tool\n"
  736.         " * is invoked.\n"
  737.         " */\n\n", inc);
  738.     fprintf(inc,"#define  STRINGS_ID         \"%s%u\"\n"
  739.      , id, STRING_RESOURCE_MINOR_ID);
  740.  
  741.     startResource(dat, RES_ID_STRINGS, STRING_RESOURCE_MINOR_ID);
  742.         /* Preamble of STRINGS.DAT file */
  743.     fprintf(dat, "%s%u", id, STRING_RESOURCE_MINOR_ID);
  744. /*    fwrite(id, sizeof(id) - 1, 1, dat);        *//* file contents ID */
  745.     fwrite("\r\n\x1a", 4, 1, dat);            /* text file full stop */
  746.     fputs("#define  STRINGS_ID_TRAILER 4\n", inc);    /* 4 additional bytes */
  747.     fputs("\n\n", inc);                        /* delimiter */
  748.  
  749.         /* parameters of strings */
  750.     fwrite(&maxCnt, sizeof(maxCnt), 1, dat);    /* number of strings */
  751.     lsize = (string_size_t)size;
  752.     fwrite(&lsize, sizeof(lsize), 1, dat);        /* total size of string text */
  753.  
  754.         /* string control area */
  755.     fwrite(string, sizeof(string[0]), maxCnt, dat);
  756.     /* append the strings */
  757.     for(cnt = 0; cnt < maxCnt; ++cnt) {
  758.         fwrite(strg[cnt].text, string[cnt].size, 1, dat);
  759.         if(makeLib)
  760.             fprintf(inc, "extern const char %s[];\n", strg[cnt].name);
  761.         fprintf(inc, "#define  %-34s 0x%02x  /* @ 0x%04x */\n"
  762.          , strg[cnt].name, cnt, string[cnt].index);
  763.     }
  764.     fputs("\n/* END OF FILE */\n", inc);
  765.     endResource(dat);
  766.  
  767.     fflush(dat);
  768.     if(ferror(dat)) {
  769.         fputs("Unspecific write error into " fDAT "\n", stderr);
  770.         return 38;
  771.     }
  772.     fflush(inc);
  773.     if(ferror(inc)) {
  774.         fputs("Unspecific write error into " fH "\n", stderr);
  775.         return 39;
  776.     }
  777.  
  778.     fclose(dat);
  779.     fclose(inc);
  780.  
  781.     if(makeLib) {
  782.         mkdir(stringdir);
  783. #define fdmake inc
  784. #define ftc101 dat
  785.         cfilename[-1] = '\\';
  786.         strcpy(cfilename, fDMAKEFILE);
  787.         if((fdmake = fopen(cfile, "wt")) == NULL) {
  788.             pxerror("creating ", cfile);
  789.             return 100;
  790.         }
  791.         strcpy(cfilename, fTCMAKEFILE);
  792.         if((ftc101 = fopen(cfile, "wt")) == NULL) {
  793.             pxerror("creating ", cfile);
  794.             return 101;
  795.         }
  796.  
  797. puts("FIXSTRS: building STRINGS library source files");
  798.         /********************** prologue */
  799.         fputs("\
  800. MAXLINELENGTH := 8192\n\
  801. # Project specific C compiler flags\n\
  802. MYCFLAGS_DBG = -UNDEBUG $(null,$(DEBUG) $(NULL) -DDEBUG=1)\n\
  803. MYCFLAGS_NDBG = -DNDEBUG=1 -UDEBUG\n\
  804. MYCFLAGS = $(null,$(NDEBUG) $(MYCFLAGS_DBG) $(MYCFLAGS_NDBG))\n\
  805. \n\
  806. #    Default target\n\
  807. all: $(CFG) strings.lib\n\
  808. \n\
  809. strings.lib .LIBRARY : ", fdmake);
  810.  
  811.         /********************* individual files */
  812.         for(cnt = 0; cnt < maxCnt; ++cnt) {
  813.             dumpString(cnt);
  814.             fprintf(fdmake, "\\\n\t" objfmt, cnt);
  815.         }
  816.         for(cnt = 0; cnt < maxCnt - 1; ++cnt)
  817.             fprintf(ftc101, "+" objfmt " &\n", cnt);
  818.         fprintf(ftc101, "+" objfmt " \n", cnt);
  819.         /********************** epilogue */
  820.  
  821.         fputs("\n\
  822. \n\
  823. .IF $(CFG) != $(NULL)\n\
  824. \n\
  825. CONFIGURATION = $(CONF_BASE)\n\
  826. \n\
  827. .IF $(_COMPTYPE) == BC\n\
  828. CONF_BASE =    \\\n\
  829. -f- \\\n\
  830. -I$(INCDIR:s/;/ /:t\";\")    \\\n\
  831. -L$(LIBDIR:s/;/ /:t\";\")    \\\n\
  832. -w\n\
  833. \n\
  834. .IF $(_COMPILER)==BC5\n\
  835. CONFIGURATION += -RT- -x-\n\
  836. .ENDIF\n\
  837. \n\
  838. CONF_DBG =    $(MYCFLAGS_DBG)\n\
  839. CONF_NDBG =    $(MYCFLAGS_NDBG)\n\
  840. \n\
  841. .ENDIF\n\
  842. \n\
  843. .IF $(_COMPTYPE) == TC\n\
  844. CONF_BASE =    \\\n\
  845. -I$(INCDIR:s/;/ /:t\";\")    \\\n\
  846. -L$(LIBDIR:s/;/ /:t\";\")    \\\n\
  847. -w\n\
  848. \n\
  849. CONF_DBG =    $(MYCFLAGS_DBG)\n\
  850. CONF_NDBG =    $(MYCFLAGS_NDBG)\n\
  851. \n\
  852. .ENDIF\n\
  853. .ENDIF\n", fdmake);
  854.  
  855.         fflush(ftc101);
  856.         if(ferror(ftc101)) {
  857.             puts("Unspecific error writing to " fTCMAKEFILE);
  858.             return 104;
  859.         }
  860.         fclose(ftc101);
  861.         fflush(fdmake);
  862.         if(ferror(fdmake)) {
  863.             puts("Unspecific error writing to " fDMAKEFILE);
  864.             return 105;
  865.         }
  866.         fclose(fdmake);
  867.     }
  868.  
  869.     return 0;
  870. }
  871.